home *** CD-ROM | disk | FTP | other *** search
/ Giga Games 1 / Giga Games.iso / net / go / prog / protocol < prev    next >
Encoding:
Text File  |  1993-06-20  |  65.2 KB  |  1,931 lines

  1. This Go modem protocol was developed by Bruce Wilcox, with input from
  2. David Fotland, Anders Kierulf, and other computer Go Programmers.  It will
  3. be avilable in the next releases of NEMESIS and Many Faces of Go.
  4. We hope that all Go programs will implement this protocol
  5. so that all Go program users will be able to play go by phone, no matter
  6. which program they own, and so that computer/computer competitions can be
  7. played without needing an operator to type moves back and forth.
  8.  
  9. The protocol is followed by the code used to implement it from Nemesis and
  10. Many Faces.  We make this code available royalty free, for use for any purpose,
  11. commercial or otherwise.
  12.  
  13. NEMESIS and Many Faces will use this protocol in the upcoming North American
  14. Computer Go Championship at the Go Congress.  We hope that other programs
  15. at this competition will also implement it.
  16.  
  17. Bruce volunteers to help with final testing of your modem protocol code.
  18.  
  19. -David Fotland (Author of Many Faces of GO)
  20.  
  21.  
  22.  
  23.     Standard Go Modem Protocol - Revision 1.0
  24.  
  25. The Standard Go Modem Protocol is intended to facilitate 
  26. computerized remote Go play and discussion between  two players. 
  27. Tranmission requirements are: 8 data bits, 1 stop bit, no parity.  
  28. Baud rate to be agreed upon by the players. The protocol consists of 
  29. two parts: a message format and a usage protocol.
  30.  
  31. The protocol is maintained by  Bruce Wilcox of Toyogo, Inc.  If you 
  32. need new codes (e.g., query, extended command) assigned, call or fax 
  33. for them.  In North America call toll-free 800-869-6469. Otherwise 
  34. call 808-396-5526 or Fax 808-396-4126. Or write to: PO Box 
  35. 25460, Honolulu, HI 96825
  36.  
  37. Features of the protocol: 
  38.  
  39. 1. Easy parsing.  Independent of any prior characters, you can 
  40. distinguish talk text, start of packet message, and other packet 
  41. bytes.  Knowing when a message is done is easy.  All required 
  42. commands take 4 bytes. Required input buffer size is only 4 bytes.  
  43. Additional incoming characters can be discarded or, if talk, echoed 
  44. to user's talk window. Format does not conflict with control 
  45. characters (e.g., Ctrl-S and Ctrl-Q).
  46.  
  47. 2. Strong text support. Text can be optionally checksummed, if 
  48. getting it correct is important. Support for multi-byte foreign 
  49. languages.
  50.  
  51. 3. High reliability. Checksum is large relative to  packet size, and 
  52. additional checks exist on some critical commands. Packets 
  53. are small. Programs can also autoverify board and time 
  54. correspondence.
  55.  Programs detecting errors of other program or unable to handle 
  56. messages of some kind can tell foe via DENY.
  57.  
  58. 4. Minimal modem traffic. No continuous token passing, only passing 
  59. signals to execute and acknowledge commands.
  60.  
  61. 5. Extensibility. Provisions for program specific extensions (via 
  62. Extension command).
  63.  
  64. 6. Future Compatibility .  Ability to recognize limitations of foe 
  65. program and retain backward compatibility in the face of protocol 
  66. revisions via QUERY and ANSWER comands.
  67.  
  68. 7. Simple code.  The minimal set is easy. Send and receive {OK,  
  69. MOVE, TAKEBACK, NEWGAME}, respond with ANSWER of 0 to any 
  70. QUERY, and cancel any command you have pending if you receive a 
  71. DENY. Don't do anything with incoming talk, or transmit outgoing 
  72. talk. 
  73.  
  74. Message Format
  75.  
  76. The standard packet message is 4 bytes, consisting of a header byte 
  77. with sequencing information, a checksum byte, and a two-byte 
  78. command. Individual text bytes intended as optional TALK for the 
  79. other's talk window may be sent outside of the packet and are not 
  80. subject to synchronization and checksum protection. However if the 
  81. language involved requires multiple bytes per character (e.g., 
  82. Japanese), such messages must be transmitted using the extended 
  83. String command to insure character sequencing is preserved.  All 
  84. packet bytes but the packet start byte have their top (sign) bit 
  85. turned on. The discussion below assumes the two machines/players 
  86. are labelled you and him. 
  87.  
  88.     1. Start Byte- 0000 00hy  
  89. The start byte has 6 fixed bits, his sequence bit (h) and your 
  90. sequence bit (y). The sequence bits  determine if his incoming 
  91. command is old or new, and whether he has seen or not seen your 
  92. most recent command sent to him. Maximizing the fixed bits 
  93. minimizes mistaken start byte recognition. If a new start byte is 
  94. detected before the full 4 bytes of a previous packet are read, the 
  95. old bytes are discarded. 
  96.  
  97.     2. Checksum Byte - 1sss ssss 
  98. Top bit always on. Checksum (s)  of any message (including the 
  99. variable length Extended command)  is created by summing into 
  100. unsigned int  the 1st, 3rd and 4th message bytes, and using only the 
  101. bottom 7 bits. If a program receives a command with a bad 
  102. checksum, it should just discard the message.
  103.  
  104.     3,4 Command 2-byte - 1ccc rvvv 1vvv vvvv
  105. Top bit of each byte is always on. ccc is 3-bit command (8 basic 
  106. commands). vvv vvv vvvv is a 10-bit value for use with commands. A 
  107. reserved (r) bit of 0 separates the two fields. TEST FOR IT. Future 
  108. commands or data or whatever may use it and break your code if you 
  109. don't.  Types are:
  110.  
  111.     0. OK - 1000 0111 1111 1111
  112. Value must be all 1's.  OK means I got your command ok, and is given 
  113. whether or not the command is executed without error by recipient. 
  114. This releases sender, who cannot transmit  any new commands until 
  115. he receives it. ANYTIME you have been sent a command and are 
  116. expected to send an OK, you may instead send a command with a new 
  117. sequence number for you and his current sequence number.  This acts 
  118. as an implicit OK, and a command to him which cannot be in conflict 
  119. with a new command from him (see CONFLICT at the end of command 
  120. list).  This property is explicitly used in the Query-Answer protocol. 
  121. DENYis an alternate response instead of OK. OK is not a true 
  122. command. You do not send back an OK in response to OK. No response 
  123. is needed or expected. You also do not send back OK in response to a 
  124. QUERY. You must send an ANSWER command.
  125.  
  126.     1. DENY command - 1001 0000 1000 0000
  127. If you wish to reject a command (can't do it or whatever), you can 
  128. deny it by sending back the DENY command instead of an OK. He 
  129. should treat your deny just as he would conflict, by retracting his 
  130. command. However he does not revert his or your sequence ids. As a normal
  131. command, DENY is rebroadcast until he sends you an OK (explicit or 
  132. implicit), and he will undo his command and stop broadcasting it to 
  133. you whenever he sees your denial. Don't DENY a denial. If a command 
  134. is part of a series of commands to be sent (not a part of an extended 
  135. multiple command), the sender should cancel sending the remaining 
  136. queued up commands. E.g., if a game record is being sent as a series 
  137. of commands, sending it should stop as soon as a move is denied. If a 
  138. denial is given on an extended multiple command, all of its 
  139. subcommands must be undone.
  140.  
  141.      2. NEWGAME command - 1002 0000 1000 0000
  142. Clear your board back to empty, and clear all query knowledge. The 
  143. sender guarantees he has the correct conditions. A good (but not 
  144. required) response to NEWGAME would be to use QUERY instead of OK, 
  145. to discover the playing conditions. If you are unable to query, it is 
  146. presumed the conditions are already set up correctly.  As soon as the 
  147. OK is complete (either to NEWGAME or to a series of queries 
  148. followed by the final answer) Black may send his first move.  It is 
  149. important to wait for an OK, instead of embedding the first move 
  150. command as an implicit ok, to allow both sides to complete any 
  151. querying they want to do. If a DENY is received in the query process, 
  152. the game is aborted. Whoever is BLACK should send the NEWGAME on 
  153. startup, but the recipient should not assume from the receipt of 
  154. NEWGAME that he is WHITE, but should use the query system instead 
  155. or have been set up. [If two humans play, then it doesn't matter who 
  156. initiated the NEWGAME, but if two computers are playing, you want 
  157. the game to automatically start in an orderly fashion. Therefore, 
  158. behavior on startup is that whoever is to be BLACK send the 
  159. NEWGAME, and whoever is to be WHITE sends no commands until it is 
  160. received (he may talk all he wants). ]
  161.  
  162. NEWGAME can be sent in the middle of a game, in which case it 
  163. terminates the game 
  164. and starts another (game parameters determined by sender).
  165.  
  166.     3. Query command- 1011 0sqq 1qqq qqqq
  167. THE QUERY COMMAND IS OPTIONAL. YOU DO NOT HAVE TO MAKE A 
  168. QUERY. The command asks a question, and the response comes in a 
  169. corresponding Answer command. Do not send an OK when you receive 
  170. a query. Send only an ANSWER. Otherwise your answer might get 
  171. conflicted and be lost.  When an answer is received by the querier, 
  172. he, in turn, will need to acknowledge it with an OK.  If the querier 
  173. does not like the answer he got, he should send back a DENYinstead 
  174. of an OK, which indicates the answer is unacceptable. The only query 
  175. with a fixed meaning is the "What game are you" query. The meaning 
  176. an interpretation of all other queries depends upon the game 
  177. selected. Toyogo is not a clearinghouse for the meaning of queries in 
  178. games other than Go.
  179.  
  180. Bit s is on if the question is "Do you support extended command q?"
  181. Answer is:   15 - the answer is Yes    0- the answer is No.
  182.  
  183. Bit s is off if the question is one of the following:
  184.  
  185.     0 = What game are you playing?
  186. Answer 0 = unknown 1 = Go  2 = Chess  3 = Othello . If your program 
  187. only supports one kind of game, this question is unimportant to it.
  188.  
  189.     1 = How big is your modem buffer ?
  190. Answer is multiplier of 16 bytes above the minimal 4 byte buffer.  
  191. E.g., 0 = 4 byte minumum  1= 20 bytes  2= 36 bytes 
  192.  
  193.     2 = What version of the protocol do you understand?
  194. Answer is 0, the initial version.
  195.  
  196.     3 = How many stones on your board? 
  197. Answer is 0 is if you have no answer, or else its the number of 
  198. stones on the board after you finish executing any command awaiting 
  199. OK.  Don't ask before the first voluntary move has been played; 
  200. otherwise the answer is not well defined (he may not have put 
  201. handicap stones on the board yet).
  202.  
  203.     4 = How much time has Black spent ?
  204. Answer is 0 if you have no answer, or the number of minutes 
  205. (rounded) spent by Black. There is no requirement that the Queryer 
  206. use the answer. If he does, the recommended use is to set Black's 
  207. clock to the minimum of the answer and the Queryer's own value. 
  208. Time spent is the question, not time used, for two reasons. First, the 
  209. value field is not interpreted as a signed number and Black could 
  210. have spent his entire time allotment (be in byo-yomi or worse). 
  211. Second, programs do not necessarily support the same time limits as 
  212. interface choices. But if they support time, they know how much has 
  213. been spent. Range is up to 1023 minutes (about 17 hours), error 
  214. between programs is no more than 1 minute.
  215.  
  216.     5 = How much time has White spent?
  217. Same concept as 4 above.
  218.  
  219.     6 = What character set do you use?
  220. Answer: 0 = unknown  1= English (ascii)  2= Japanese.  Other codes 
  221. assigned on request.
  222.  
  223. If the querier can match the language he should do so. If he cant,  
  224. text defaults to the character sets of each machine (best of luck). If 
  225. the language is a multi-byte character language (e.g., Japanese), text 
  226. sent outside of the packets is in English. Text sent using the 
  227. extended String command is multi-byte oriented (format currently 
  228. unspecified) and represents that language.  Accidental dropping of 1 
  229. byte of a multibyte string destroys the meaning of all other bytes, 
  230. hence passing multi-byte languages via the checksummed packet is 
  231. required. The notion of character set is used in supporting talk  
  232. between two programs echoed into a talk window. Supporting talk is 
  233. optional.
  234.  
  235.     7 = What rules are you using?
  236. Answer: 0 = unknown 1 = Japanese 2 = Ing Chinese (SST laws).
  237.  
  238.     8 = What handicap are you set for?
  239. Answer: 0 = unknown 1 = even 2..n are handicaps.  See discussion of 
  240. handicaps under MOVE command.
  241.  
  242.     9 = What is the board size?
  243. Answer: 0 = unknown.  N > 0 is NxN board size.
  244.  
  245.     10 = What is the time limit per player
  246. Answer: 0 = unknown.  N>0 is time in minutes.
  247.  
  248.     11 = What color is the computer playing on your side.
  249. Answer: 0 = unknown. 1 = White 2 = Black
  250.  
  251.      12 = who are you ?
  252. Answer is:. 0- unknown  1- NEMESIS   2- MANY FACES OF GO  3-
  253. SMART GO BOARD  4-GOLIATH   5- GO INTELLECT   6- STAR OF 
  254. POLAND.  Other ids will be assigned in future on demand. Contact 
  255. Toyogo. If a program needs to identify its version number, a new 
  256. Version # query will be created.
  257.  
  258. SETUP AGREEMENT:  The game setup parameters can be established 
  259. between the two programs by query/answer. Whoever gets the first 
  260. query command through can thereafter read off all the settings of 
  261. the other program and either verify that they  match, set himself up 
  262. accordingly, or issue a DENY.  If the querier gets back a 0 response, 
  263. either he doesn't care, or he stops proceeding into the game.  How 
  264. the initial query command occurs and by whom is not specified 
  265. (since it is not required to happen), but subject to program 
  266. implementation. Be reminded that this feature may not even be 
  267. supported by a program. In that case it is the responsibility of the 
  268. two humans to see that the programs are set up correctly.
  269.  
  270.     4. Answer command - 1100 0aaa 1aaa aaaa
  271. THE ONLY REQUIRED ANSWER IS 0, if you haven't implemented any 
  272. other answers. Sent back in response to QUERY, instead of OK. See 
  273. Query for interpretation of a. A program is not required to QUERY, 
  274. nor to do anything with the answer.   If you receive an ANSWER, you 
  275. must acknowledge it with an OK.
  276.  
  277.     5. Move command - 1101 0pii 1iii iiii
  278. Interpretation of the move command is game specific. For the game 
  279. of Go, top value bit (p) is 1 if player to move is white or 0 if black.   
  280. 9 bit value (i)  is a board intersection value or 0 for pass.  
  281. Intersection values run 1 at A1, 2 at B1 ... with value wrapping at 
  282. end of row (varies with board size) so next row is A2.  Range for 
  283. 19x19 board is thus 1...361 (A1 ...T19). A1 is in the lower left corner
  284. and the letter I is omitted.  A program is required to 
  285. remember all the moves from the root position to the current 
  286. position. It may, but is not required to, remember moves played later 
  287. than the current position which have been taken back.  A  move 
  288. always extends the current record of moves as the next move in 
  289. sequence. A dumb program keeps no history of moves taken back, so 
  290. extending the current move sequence is a simple matter. Complex 
  291. programs may track variations or continuations, and may need to 
  292. decide whether such a sequence is a replacement of the retracted 
  293. moves, or a variant or what.  Such decisions are the domain of the 
  294. individual program. The program must only insure that the board 
  295. stays consistent between the two programs. Also, while one may 
  296. assume a program will not transmit an illegal move, the receiving 
  297. program may check for that and issue a DENY instead of an OK if such 
  298. a move is detected.  
  299.  
  300. Moves transmitted are moves that players (human or computer) have 
  301. a choice about. See handicaps.
  302.  
  303.  
  304. Handicap moves:
  305. Handicap stones are a particularly thorny problem. The underlying 
  306. premise of the protocol is that  a pair of computers is shared 
  307. cooperatively. Either player may place any color stones at any time, 
  308. so there is no inherent program knowledge of who is Black and who 
  309. is White. Therefore it is not clear who should send handicap moves 
  310. to whom. Both programs might send the other messages about 
  311. handicap stones, either in lump, in conflict, or intermixed with each 
  312. other.  Also, programs may represent handicap moves as either a 
  313. contiguous sequence of Black moves, or a sequence of a Black move 
  314. and White passes. Also handicap stones may be represented 
  315. internally from other moves. The receiving program cannot tell what 
  316. kind of move a stone is unless it knows the handicap in advance. So, 
  317. with that can of worms in mind:
  318.  
  319.   Japanese Handicap Rules:
  320.     Under Japanese rules, programs DO NOT transmit handicap setup 
  321. stones. The first move transmitted will be White's first move. The 
  322. placement of handicap stones for handicaps greater than 1 under 
  323. Japanese rules on a 19x19 board use these intersections in order: D4 
  324. Q16 D16 Q4 (K10 when handicap is odd)  D10  Q10 K4 K16. E.g., 2 
  325. stones at D4 & Q16, 5 stones at D4, Q16, D16, Q4, K10, 6 stones at 
  326. D4, Q16, D16, Q4, D10, Q10. On odd board sizes of 13x13 and up, the 
  327. corner handicaps are always on the 4th line and the center . On the 
  328. 9x9 and 11x11, handicaps are on the 3rd line and the center. Order of 
  329. placement always mimics the 19x19 case. Handicaps on  even board 
  330. sizes, sizes under 9x9, or handicaps beyond 9 stones are not 
  331. predetermined by the standard.
  332.  
  333. Reminder, if you transmit the moves of a game record, don't 
  334. transmit the handicap stones under Japanese rules .
  335.  
  336.   Chinese Handicap Rules:
  337.     The handicap passes by White are not transmitted. 
  338.  
  339. Transmitting game records:
  340. How a handicap game record under Japanese rules can be transmitted 
  341. depends upon the sophistication of the receiving program. To 
  342. transmit a game record, first transmit the NEWGAME command. Then, 
  343. if the receiving program is sophisticated, it can query for the 
  344. handicap and set itself up appropriately. The handcap under Japanese 
  345. rules is not transmitted. If the receiving program is dumb, it will 
  346. not know about the handicap.  So you determine if he has asked you, 
  347. and if not, you should ask him about his handicap prior to sending 
  348. your recorded moves. If his handicap is set up correctly, send the 
  349. moves w/o the handicaps. If his handicap is anything but correct, 
  350. decline to send. If you want, if his handicap is even, you could send 
  351. him all moves including the handicap, but the standard does not 
  352. require it. If you want to take the lazy approach, always transmit 
  353. the game as an even game.
  354.  
  355.     6. Take back move command- 1110 0ttt 1ttt tttt
  356. The meaning of taking back a move is game specific. In Go, it is 
  357. taking back a move a player had a 
  358. choice about. Under Japanese rules  Black's handicap moves are fixed 
  359. and cannot be taken back.  Under Chinese rules White's passes are 
  360. required. Hence, taking back a move means removing a Black handicap 
  361. stone, and any White passes needed to accomplish this. 
  362.  
  363. Value t is how many moves to retract, and ranges from 0 to 1023. 
  364. Taking back 0 has no effect. A turn may consist of multiple moves 
  365. (e.g., the SETUP command in "Standard Format" for text records. The 
  366. meaning of take back is retracting a move, not a turn.  If a program 
  367. is unable to obey the Take back move command given it,  it may 
  368. respond with a DENY instead of an OK.
  369.  
  370.     7. Extended command - 1111 0mmm 1mmm mmmm   1nnn nnnn 1sss ssss
  371.  
  372. SUPPORT OF EXTENDED COMMANDS IS OPTIONAL. Do not send such a 
  373. command unless you have queried and discovered that recipient 
  374. supports it. Also be aware that receiver's buffer size may not fit 
  375. everything you want to send and you might have to break it into 
  376. smaller chunks. You should determine his buffer size with a QUERY command.
  377.  
  378. The value of the extended command is how many MORE bytes follow 
  379. after the basic four. At a minimum, the value is two.  The first 
  380. following byte has the extended command name (n). The second 
  381. following byte is a checksum (s) computed for the extended part of 
  382. the message (bytes 5, 7...4+m).  Programmers wishing an assigned 
  383. name can contact Toyogo. Commands created can be optionally 
  384. supported by more than one program. Programs are not required to 
  385. support extended commands. They may discard such a message if 
  386. they wish, but if queried about such support they 
  387. must be able to answer NO.  Current extended command names are:
  388.  
  389.     0 - String 1000 0000
  390. The contents are to be displayed as talk output. They are analogous 
  391. to text sent outside of packets except 1) they are secure 
  392. (checksummed) and 2) they can represent multi-byte languages if 
  393. needed.
  394.  
  395.     1. Replay 1000 0001  1nnn nnnn  1nnn nnnn
  396. If a program keeps track of moves taken back, then this command is 
  397. the inverse of TAKE BACK. The third and fourth extended bytes (n) are the 
  398. count of how many moves to replay.
  399.  
  400.     15 - Multiple 1000 1111
  401. The contents are multiple commands (without individual header and 
  402. checksum bytes) intended to be executed as one. The purpose is to 
  403. speed up response to complex sequences of actions, and/or to insure 
  404. control is retained by sender for the duration of the sequence. OK, 
  405. DENY, QUERY, ANSWER, and the multiple extended command MAY NOT 
  406. be used within a multiple command. 
  407.  
  408. CONFLICT - If both players send commands at each other at the same 
  409. time, this is detected as a conflict. Conflict is detected when you 
  410. receive a new command from him while you are awaiting his OK, and 
  411. the new command does not have your current sequence number.  
  412. Programs detecting conflict are required to retract their command 
  413. (decrement their sequence id, stop waiting for an OK on their 
  414. command, and undo any effects their command had on them.  E.g., if 
  415. they placed the move of the MOVE command, they should unplace it. 
  416. It is not essential that both programs detect the conflict, though 
  417. they will if there are no tranmission problems.
  418.  
  419. A program which is in the middle of a sequence of commands it 
  420. wishes to send, may decide to retry transmitting its conflicted 
  421. command after a random delay (so the two programs don't stay in 
  422. conflict).  If it receives an intervening foe command before it has 
  423. completed its own list, it may issue a DENY if accepting his 
  424. command would be a problem.  Issuing a DENY prohibits him from 
  425. continuing his attempt to send you his command.  If he sends you a 
  426. DENY in the middle of your sequence, you must stop sending further 
  427. commands from that sequence. DONT DENY A DENY.
  428.  
  429. AUTOMATIC CHECKING & TIME SYNCHRONIZATION:  QUERY can be used 
  430. on a regular basis to check that the stone count matches and keep 
  431. the player times in synch.  This can be done automatically by the 
  432. program, but such commands should be kept from interfering 
  433. (CONFLICT) with user commands without good cause.  To avoid 
  434. conflict with foe user, send an automated QUERY whenever you would 
  435. send an OK.  You are not safe from your own user unless you buffer 
  436. his command for execution for when the query/answer/ok is 
  437. complete or otherwise prevent him from executing a command until 
  438. then. 
  439.  
  440. TALK TEXT outside of packets are sent with top bit off (7bit ascii 
  441. notation). CTRL G must make sound or flash or equivalent when 
  442. printed into talk area. CTRL-H  (Ascii 8, backspace) should have the 
  443. correct effect on both machines. Text is intended to be echoed into 
  444. the talk window of the other machine. Usually you will also echo the 
  445. talk into your own window as it is typed. Supporting talk windows is 
  446. not required, and a program may just discard incoming text if it 
  447. wishes.
  448.  
  449. INITIAL CONDITIONS - The protocol requirements for using the 
  450. format are as follows:
  451. 1. Sequence IDs  for you and he both start at 0.
  452. 2. Your sequence ID is incremented before creating a new non-OK 
  453. message, and is incorporated into the message. THE FIRST MESSAGE 
  454. EVER SENT WILL HAVE A START BYTE OF 0100 0001, which says foe's 
  455. last message was 0, and my new sequence value is 1.
  456. 3. Any program can spontaneously send the first command.  You 
  457. should assume a program  cannot accept any extended commands and 
  458. answers 0 to any query until you query successfully otherwise.
  459. 4. His sequence ID is taken from the incoming command after it is 
  460. successfully validated by you. 
  461. 5. When you transmit a non-OK command, you are prohibited from 
  462. transmitting any NEW commands until you receive or infer an OK 
  463. from an incoming message of his.
  464. 6. After you transmit a non-OK command, if you receive no 
  465. acknowledgement from him within some time period, you should 
  466. retransmit the same message. Retransmission changes no sequence 
  467. data. 
  468.  
  469. PARSING:  Incoming characters fall into 3 classes: text, start byte, 
  470. other command bytes.  Text is any non-start byte having the top bit 
  471. off.  If you detect other command bytes and have no corresponding 
  472. start byte, just discard the extraneous command bytes.
  473.  
  474. STATE ACTION CHART:
  475. You have just received a message. Your current sequence id is B of 
  476. {A,B}. His last sequence ID you saw was 2 of {1,2}.  What should you 
  477. do and what does it mean?
  478.  
  479. Neutral:   (sitting idle with no transmission,  you have no unfinished 
  480. commands)
  481.  
  482. OK(any)    
  483. OK(B2)  
  484.     indicates repeated OK. Others impossible. Discard all.
  485. CMD(A1,A2)
  486.     New or Old command not possible to receive.  Discard.
  487. CMD(B1)    
  488.     Normal New command. Do it + send back OK(before or after).
  489. CMD(B2)    
  490.     Old Command . He hasnt seen my OK yet. Resend last comand
  491.     (implicit OK or real OK).
  492.  
  493. OKWait:    (retransmiting your  command every n seconds until 
  494. OK received or  implied)
  495.     
  496. OK(A1,A2,B1) 
  497.     Not possible. Discard.
  498. OK(B2)    
  499.     Normal OK. Terminate OK Wait (Go to neutral state).
  500. CMD(A1)    
  501.     New Command from him, he hasn't seen my command yet.
  502.     This is conflict. Do not respond with an OK! Undo my command, 
  503.     revert my sequence id, don't update his id, and go to neutral. If he 
  504.     sees my command, he will do likewise.  If he doesn't see my 
  505.     command he will rebroadcast his and win the toss.
  506. CMD(A2)    
  507.     Old Command, he hasn't seen my new command. He was unaware of 
  508.     my OK at that time. Discard. Retransmit your command ahead of 
  509. timeout rebroadcast if you want to.
  510. CMD(B1)    
  511.     New Command from him, but he has seen my command. He must 
  512.     have sent OK and I missed it or has some reason to insure we 
  513. don't conflict. Go to neutral state and then do command+ send 
  514.     back OK (before or after).
  515. CMD(B2)    
  516.     Not possible. Discard.
  517.  
  518. TIMING:
  519. The protocol allows a program to make its own choice about when to 
  520. send a command or an OK. The most simple & inefficient way is to 
  521. send the command, wait for his OKand then execute your command. If 
  522. he sends his OK after he has completed execution, that means 
  523. delaying the time of two machines implementing the command.  
  524. However, if you do not wait for his OK before executing your 
  525. command, you must be prepared to undo the command if conflict 
  526. arises. If he can send the OK upon successful receipt of your 
  527. command, both of you can process the command in parallel.
  528.  
  529. Programs may also choose their own rebroadcast frequency. Several 
  530. seconds should be allowed for the receiving program to execute and 
  531. acknowledge the command, but there is no requirement, since 
  532. thereceiver can just discard any messages it wishes.
  533.  
  534. If both programs generate an automatic QUERY or NEWGAME on 
  535. startup the two programs may get a CONFLICT. If the programs back 
  536. off and try again later, they may still conflict.  The protocol does 
  537. not prevent an infinite loop in such a situation. Our advice is that if 
  538. you CONFLICT and you are using a loop to try again, you should select 
  539. a random delay within an ever increasing delay range.  You may need 
  540. to delay longer than the other program's rebroadcast delay to 
  541. succeed.  Alternatively after n conflicts without an accepted 
  542. command on either side, you might stop and report to the user. He 
  543. should be able to reinitiate the loop at his discretion.  We 
  544. recommend that the first command sent be a NEWGAME command by 
  545. whoever is Black. The receiver can query to establish game 
  546. parameters, and then Black can send his first move.
  547.  
  548.  
  549.  
  550. This is the code used by Nemesis to implement this protocol, written for
  551. Lightspeed C on the Mac.
  552.  
  553. #include <nemheaders>
  554. #define MaxBuffer (96 + 5)        /* 1 byte for length code, the rest for buffer */
  555. /* the code allows room to listen to some extended commands, but doesnt use them */
  556. #define Obuffer 5    /* we only write 4 byte messages (+ length) */
  557. INT pending = 0;            /* characters expected to receive to finish packet*/
  558. #define CTRL_G 0x07        /* talk recognizes CTRL_G as sound */
  559. /* 8 Commands are in COMMANDBITS */
  560. #define COMMANDBITS 0x70        /* mask holding all commands */
  561. #define OKMSG 0x00
  562. #define DENY 0x10
  563. #define RESET 0x20
  564. #define QUERY 0x30
  565. #define RESPONSE 0x40
  566. #define MOVE 0x50
  567. #define TAKEBACK 0x60
  568. #define EXTENDED 0x70
  569. #define STRING 0x00
  570. #define REPLAY 0x01
  571. /* QUERY types are */
  572. #define FEATUREBIT 0x0400 /* asks if you support extended command named */
  573. #define GAMEID 0            /* what game  1 = go */
  574. #define HOWBIG 1            /* your buffer size in 16byte units above 4 byte minimum */
  575. #define PROTOCOLID 2        /* Currently Protocol version is 0 */
  576. #define BOARDVERIFY 3        /* number of stones on board after current command finished.*/
  577. #define BLACKTIMEUSED 4        /* seconds spent by black */
  578. #define WHITETIMEUSED 5        /* seconds spent by white */
  579. #define LANGUAGE 6             /* what character set */
  580. #define WHATRULES    7             /* what rules */
  581. #define WHATHANDICAP 8
  582. #define WHATSIZE 9
  583. #define WHATTIME 10
  584. #define WHATCOLOR 11
  585. #define WHOAREYOU 12            /* what program. I am NEMESIS */
  586. /* answers are */
  587. #define NEMESIS 1
  588. #define WHITEMOVE 0x0200    /* bit on move to indicate White color */
  589. static unsigned char nconflicts = 0; /* conflicts in progress */
  590. #define RETRYDELAY (4 SECONDS)        /* how long I wait for OK before rebroadcasting */
  591. #define QUERYDELAY (120 SECONDS)    /* how often I poll to keep time consistent */
  592. unsigned char outmbuffer[Obuffer],inmbuffer[MaxBuffer];
  593. long timesent = 0;        /* to tell when to rebroadcast */
  594. static char oursequence = 0;     /* our last sequencing bit */
  595. static char hissequence = 0;     /* his last sequencing bit */
  596. static long timequery = 0;         /* to tell when to reverify */
  597. static int lastquery = -1;        /* If i have a query awaiting an answer */
  598. static unsigned int querycnt = -1; /* for cycling thru my queries */
  599. static unsigned int movecnt = -1;    /* to tell when to rebroadcast board verify */
  600. INT modemmove = -1;         /* echo expected for this location, dont send it out */
  601.  
  602. /* HOST SPECIFIC MODEM ROUTINES */
  603. VOIDFN OutModem (echo) INTM echo; {/* transfer serial output*/
  604.     long count = (long)outmbuffer[0]; unsigned int pt,val; 
  605.     /* send new message */
  606.     outuser(M_FIXTIME,0,0); /* update time */
  607.     timesent = time_count; /* when we sent the message*/
  608.     if (FSWrite(ModemOut, &count, outmbuffer+1) != noErr) return -1;}
  609.     
  610.     
  611. int InModem () {/* transfer serial input into input buffer*/
  612. /* CALLED REPEATEDLY FROM EVENT LOOP */
  613.     long one = 1,cnt; int type,val; unsigned char c;
  614.        SerGetBuf(ModemIn, &cnt); /* bytes available to be read */
  615.     while (cnt-- > 0) { /* read all available characters in buffer */
  616.         if (FSRead(ModemIn, &one, &c) != noErr) {/* failed to read */
  617.             return -1;}
  618.         if (c > 3 && c < 128) {/* talk character */
  619.             if (pending) pending = inmbuffer[0] = 0;
  620.             rawabsorb(c);
  621.             continue;}
  622.         
  623.         /* start of packet recognized -- will need 4 bytes */
  624.         if (c < 4) {
  625.             pending = 4;
  626.             inmbuffer[0] = 0;}
  627.             
  628.         /* Have a command byte, discard if not expected, absorb if expected*/
  629.         if (pending){ 
  630.               inmbuffer[++inmbuffer[0]] = c; /* move it to buffer */
  631.               --pending;
  632.             if (inmbuffer[0] == 4) { /* have 1st 4 bytes complete */
  633.                 if (!checksum()) { /* BAD CHECKSUM */
  634.                     inmbuffer[0] = 0;
  635.                     continue;}
  636.                 type = gett(inmbuffer); /* lets see if it is an extended command */
  637.                 val = getv(inmbuffer); /* and get size */
  638.                 if (type == EXTENDED) {
  639.                     if ((val + 5) <= MaxBuffer) pending = val;/* accept read*/
  640.                     else {
  641.                         senddeny("extended too big");
  642.                         inmbuffer[0] = 0;}}}}}
  643.     return dominput();}/* see what you have */
  644.  
  645.  
  646.  
  647. /* GENERIC MODEM ROUTINES */
  648.  
  649.  
  650. INTFN resetmodem(
  651.     ) {/* clear the modem interface */
  652.     
  653.     modemwaiting = inmbuffer[0] =     oursequence = hissequence = 0;}
  654. static VOID mylisten(text) char *text; {
  655.     listen(text);} /* this sends text to my talk window */
  656. static VOID myoutmodem(n)  INTM n; {
  657.     OutModem(n);}
  658. VOIDFN SendTalk(c) char c; {/* our keyboard letter we sent to us and him */
  659.     char msg[2];
  660.     msg[0] = c;
  661.     msg[1] = 0;
  662.     mylisten(msg); /* tell my talk */
  663.     outmbuffer[0] = 1;
  664.     outmbuffer[1] = c;
  665.     myoutmodem(FALSE);} /* tell out modem */
  666.     
  667. static void xmit(OK) INTM OK; {/* add header into message */
  668.     unsigned int sum;
  669.     outmbuffer[0] = 4; /* message is this long */
  670.     if (!OK) oursequence ^= 1; /* flip our sequence */
  671.     outmbuffer[3] |= 0x80; /* required  bit */
  672.     outmbuffer[4] |= 0x80; /* required bit */
  673.     sum = outmbuffer[1] =  (hissequence << 1) | oursequence;
  674.     sum += outmbuffer[3] + outmbuffer[4];
  675.     outmbuffer[2] = sum | 0x80; /* put out checksum */
  676.     myoutmodem(TRUE);} /* send the completed message */
  677.             
  678. static void sendcmd(cmd,val) INTM cmd,val; {/* send out command */
  679.     if (cmd == QUERY) lastquery = val;
  680.     outmbuffer[4] = val & 0x7f;
  681.     outmbuffer[3] = (val >> 7) & 0x03;
  682.     outmbuffer[3] |= cmd;
  683.     xmit(0);
  684.     modemwaiting = 2;}
  685.     
  686. VOIDFN senddeny(msg) char *msg; {
  687.     /* msg just tells us why we are denying him */
  688.     sendcmd(DENY,0);}
  689.     
  690. static void sendok(type) INTM type; {/* ok his last message */
  691.     nconflicts = 0;
  692.     
  693.     /* automatic polling, unless he is querying us */
  694.     if (type == QUERY); /* requires RESPOND and not ack so dont */
  695.     else if ((movecnt % 10) == 9) { /* auto poll for stone count periodically */
  696.         sendcmd(QUERY,BOARDVERIFY);/* doublecheck the board */
  697.         ++movecnt;}
  698.     else if (G(NOWPLAYING) == S_PLAYING && (time_count - timequery) > QUERYDELAY) {/* low level status first */
  699.         sendcmd(QUERY,(querycnt & 1) ? BLACKTIMEUSED : WHITETIMEUSED);
  700.         timequery = time_count;} /* dont query again for a while */
  701.     else {
  702.         outmbuffer[3] = 0x07; /* ack command, reserved bit, and message */
  703.         outmbuffer[4] = 0xff; /* ack message */
  704.         xmit(TRUE);}}/* send OK */
  705.     
  706. VOIDFN SendMove (
  707.     pt,multi) INTM pt,multi; {/* move encoded color etc */
  708.     register INT loc = LOCATION(pt), color = WHOPLAYED(pt),where; char junk[30];
  709.     where = loc;
  710.     /* if move is during handicap setup, dont echo forced moves */
  711.     /* tested before move is placed on board */
  712.     if (G(HANDICAP) > 1 && gethandicap() < G(HANDICAP)) return TRUE; /* not enough black stones yet*/
  713.             
  714.     if (where) where = X(where) + ((Y(where) - 1) * G(BDSIZE)); /* grid ref */
  715.     ++movecnt;        /* autopoll of stone count*/
  716.     sendcmd(MOVE + ((color == WHITE) ? 0x04 : 0),where);}
  717.             
  718. VOIDFN SendNewGame(
  719.     ) { /* clear */
  720.     
  721.     sendcmd(RESET,0);}
  722.         
  723. VOIDFN SendUnmove(
  724.     n) INTM n; {/* retract this many moves */
  725.     
  726.     ++movecnt;            /* autopoll stone count */
  727.     sendcmd(TAKEBACK,n);} /* only done by a user anyway */
  728. VOIDFN rawabsorb(c) unsigned char c; { /* echo what he ships immediately*/
  729.     
  730.     char msg[2];
  731.     if (c == CTRL_G) outuser(DOSOUND,OTHERBEEP,0); /* sound beep  */
  732.     else  {
  733.         msg[0] = c;
  734.         msg[1] = 0;
  735.         mylisten(msg);}} /* says his message */
  736.         
  737. INTFN checksum() {
  738.     unsigned char c;
  739.     c =  ((inmbuffer[1] + inmbuffer[3] + inmbuffer[4]) | 0X0080) & 0x00ff;
  740.     return (c == inmbuffer[2]);}
  741.         
  742. INTFN gett(buf) unsigned char *buf; {
  743.     return buf[3] & COMMANDBITS;}    /* 3 bit type */
  744. INTFN getv(buf) unsigned char *buf; {
  745.     return (buf[4] & 0x7f) | ((buf[3] & 0x07) << 7);} /* 10 bit value field */
  746. static void undolast(deny) INTM deny; {/* take back last command */
  747.     register INT type = gett(outmbuffer), val = getv(outmbuffer); INT tmp;
  748.     lastquery = -1; /* there is no query pending now */
  749.     
  750.     if (type == MOVE) {
  751.         if (deny) setflag(NOWPLAYING,S_STOPPED); /* stop trying */
  752.         unmove();}
  753.     else if (type == TAKEBACK) {
  754.         tmp = NTURN + val;
  755.         while (NTURN < tmp) doforward(0);} 
  756.         
  757.     /* nothing to do for QUERY, RESPOND, EXTENDED */
  758.     }
  759. static INT bdcount() {
  760.     register INT i,n = 0;
  761.     SWEEP361 if (ISSTONE(i)) ++n;
  762.     return n;}
  763.     
  764. static INT inpmove(val) INTM val; {
  765.     register INT tmp,pt,ans;
  766.     tmp = (val & WHITEMOVE) ? WHITE : BLACK; /* who is to play */
  767.     pt = LOCATION(val) - 1;
  768.     pt = MAKECOORD( (pt % G(BDSIZE)) + 1, (pt / G(BDSIZE)) + 1);
  769.     if (!legalcheck(pt,tmp)) { /* illegal move */
  770.         senddeny("illegal move"); /* send a denial */
  771.         return 0;}
  772.     if (tmp != nextplayer()) G(FLIPTURN) =  NTURN; /* switch mover */
  773.     cmdval = pt;
  774.     cmd2val = 0;
  775.     modemmove = pt; /* we got this by modem -- clear it when it echos*/
  776.     ++movecnt;
  777.             
  778.     ans = ClickedAtPoint(pt,0,0,0); /* see if incoming move has legal context (his move) */
  779.     if (!ans) senddeny("not his turn"); /* send a denial */
  780.     return ans;}
  781.     
  782. static int nemabsorb() { /* eat nemesis data */
  783.     INT i,j,ans = 0,pt,val = getv(inmbuffer),type = gett(inmbuffer),him,us,tmp,oldquery = lastquery;
  784.     unsigned int sum; long time;
  785.  
  786.     us = (inmbuffer[1] & 0x02) >> 1;    /* his last seen message of ours */
  787.     him = inmbuffer[1] & 0x01;            /* his current message id */
  788.     /* VALIDATE OK COMMAND */
  789.     if (type == OKMSG) { /* OK command */
  790.         if (us != oursequence || him != hissequence); 
  791.         else if (!modemwaiting); 
  792.         else  nconflicts = modemwaiting = 0; /* normal OK */
  793.         goto exit;}
  794.         
  795.     /* validate command */
  796. /* STATE: NEUTRAL */
  797.     if (!modemwaiting) {/* we are in neutral */
  798. /* CASE 2: he is repeating an old command */
  799.         if (him == hissequence) {
  800.             /* CASE B: he knows our last command but has missed our OK */
  801.             if (us == oursequence)     myoutmodem(TRUE); /* repeat OK as an echo*/
  802.                  /* he didnt see our OK, help him */
  803.             /* CASE A: he doesnt know our last command -- since it cleared, not possible */
  804.             ;
  805.             goto exit;}
  806. /* CASE 1: he is sending a new command */
  807.         /* CASE A: he doesnt know our last command, but it cleared - not possible */
  808.         else if (us != oursequence) {/* his new command does not recognize our old command*/
  809.             goto exit;}
  810.         /* CASE B drops thru: he has new message and he saw our last command so he is ok.*/}
  811. /* STATE: OK_WAIT */
  812. /* CASE B: he knows our last command */
  813.     else if (us == oursequence) { /* we have a command pending, he has seen our command */
  814.         /* CASE 2: he sends old command-- cant do that and know of our command */
  815.         if (him == hissequence) {/* he repeats his old command */
  816.             goto exit;} /* not possible */
  817.         /* CASE 1: he sends new command knowing of our old one-- implicit OK */
  818.         else modemwaiting = FALSE;} /* we must have missed his OK */
  819.     
  820. /* CASE A: he does not know of our command yet */
  821.     else { /* he has not seen our command */
  822.         /* CASE 2: he sends old command-- he missed OK */
  823.         if (him == hissequence) myoutmodem(TRUE);/* repeated old cmd, our resend will fix him*/
  824.         /* CASE 1: he sends new command */
  825.         else { /* His Command in Conflict with ours */
  826.             ++nconflicts;
  827.             oursequence ^= 1; /* revert to before we shipped command */
  828.             /* leave his sequence alone-pretend we didnt hear his command */
  829.             /* he will hear us and quit, or echo and win. */
  830.             modemwaiting = FALSE;
  831.             undolast(nconflicts > 4);}/* UNDO COMMAND */
  832.         goto exit;} /* ignore his command */
  833.         
  834.     hissequence = him; /* we recognize his command */        
  835.     /* screen his command for legality with us */
  836.     if (type == MOVE) { /* verify a move loc or pas loc */
  837.         ans = inpmove(val); /* setup move or decline */
  838.         if (!ans) goto exit;}
  839.     else if (type == TAKEBACK) {
  840.         if (NTURN < val) {
  841.             senddeny("takeback too big"); /* deny */
  842.             goto exit;}
  843.         modemmove = 1000;} /* came by modem */
  844.     else if (type == DENY) {
  845.         undolast(TRUE);}
  846.     else if (type == EXTENDED) {
  847.         senddeny("Extended not accepted");
  848.         goto exit;}
  849.                 
  850.     /* send an acknowledge *//* the command is self-consistent */
  851.     sendok(type);    /* release him (except QUERY)... we expect to do his command */
  852.     if (type == TAKEBACK) {
  853.         ++movecnt;
  854.         tmp = NTURN - val; /* go back to this turn */
  855.         while (NTURN > tmp && NTURN > 0) dobackup(TRUE);} 
  856.     else if (type == QUERY) {/* give him an answer */
  857.         tmp = 0;
  858.         /* If his query freed us from OK-wait, this will put us back again in it */
  859.         if (val & FEATUREBIT) tmp = 0; /* we dont do windows etc */
  860.         else if (val == GAMEID) tmp = 1; /* go */
  861.         else if (val == HOWBIG) tmp = (MaxBuffer - 5) / 16;
  862.         else if (val == PROTOCOLID) tmp = NEMESIS;
  863.         else if (val == BOARDVERIFY) tmp = bdcount();
  864.         else if (val == BLACKTIMEUSED) tmp = (INT) (bticks/60) ;
  865.         else if (val == WHITETIMEUSED) tmp = (INT) (wticks/60);
  866.         else if (val == LANGUAGE) tmp = 1; /* english ascii*/
  867.         else if (val == WHATRULES) tmp = G(CHINESERULES) + 1;
  868.     else if (val == WHATHANDICAP) {
  869.             tmp = G(HANDICAP);
  870.             if (tmp < 2) tmp = 1;}
  871.         else if (val == WHATTIME) tmp = G(TIMELIMIT);
  872.         else if (val == WHATSIZE) tmp = G(BDSIZE);
  873.         else if (val == WHATCOLOR) { /* what computer color */
  874.             if (bcomputer() && !wcomputer()) tmp = 2;
  875.             else if (wcomputer() && !bcomputer()) tmp = 1;}
  876.         else if (val == WHOAREYOU) tmp = NEMESIS; /* react negatively to questions */
  877.         
  878.         sendcmd(RESPONSE,tmp);}
  879.     else if (type == RESPONSE) {/* answer to my question */
  880.         if (oldquery == BOARDVERIFY) {
  881.             sprintf(myouttext,"\r<Board count mismatch he=%d me=%d>",val,bdcount());
  882.             if (val != 0 && val != bdcount()) mylisten(myouttext);}
  883.         else if (oldquery == BLACKTIMEUSED && val != 0) {/* settle on min time */
  884.             time = val * 60;
  885.             if (bticks > time) bticks = time;}
  886.         else if (oldquery == WHITETIMEUSED && val != 0) {/* settle on min time */
  887.             time = val * 60;
  888.             if (wticks > time) wticks = time;}
  889.         lastquery = -1;} /* I will not recognize future response w/o query */
  890.     else if (type == RESET) {/* refresh */
  891.         outuser(M_NEWGAME,0,0);
  892.         modemwaiting = 0;}
  893.     
  894.     /* eat off message accepted */
  895. exit: inmbuffer[0] = 0; /* we only allow 1 message in buffer */    
  896.     return ans;}
  897. INTFN dominput() {
  898.     if (inmbuffer[0] == 0 || pending) {/* no message completed yet */
  899.         if (modemwaiting) {/* we are expected a message */
  900.             if ((time_count - timesent) >= RETRYDELAY) myoutmodem(2);}} /* repeat the message as an echo*/
  901.     else if (G(MODEM_STATE) == 2 && !inside) return nemabsorb(); /* we have a command to eat */
  902.     return 0;}
  903.  
  904.  
  905.  
  906. This is the code used by Many Faces of Go to implement this protocol, written
  907. in Microsoft C 6.0 for the IBM-PC.  It includes a serial COM port interrupt
  908. handler.
  909.  
  910.  
  911. # include <stdio.h>
  912. # include "g2hd.h"
  913. # include "keys.h"
  914.  
  915. /* Copyright 1991 david fotland.  Permission granted to use this
  916.  * code for any commercial or noncommercial purposes as long as this
  917.  * copyright notice is not removed.  This code was written for 
  918.  * Microsoft C 6.0
  919.  */
  920.  
  921. #ifdef __STDC__
  922.  
  923. static void putcommand(char *com);
  924. void putamove(int ptr);
  925. int domodem();
  926. static void fillsendpacket();
  927. static void sendthepacket();
  928. void displaychar(char c, int side);
  929. static void domodemcommand();
  930. static void takeback();
  931. void puttakeback(int n);
  932. static void putquery(int query);
  933.  
  934. #else
  935.  
  936. static void putcommand();
  937. void putamove();
  938. int domodem();
  939. static void fillsendpacket();
  940. static void sendthepacket();
  941. void displaychar();
  942. static void domodemcommand();
  943. static void takeback();
  944. void puttakeback();
  945. static void putquery();
  946.  
  947. #endif
  948.  
  949.  
  950. /* modem interface routines.  putmodem() puts a single character out through
  951.  * the modem.  getmodem() gets a single character from the modem or returns
  952.  * FALSE if one is not available.  The host interface is through
  953.  * putmodem() for typed characters for the talk window, and
  954.  * putamove(), puttakeback(), and putreset() for commands.
  955.  * The host should call domodem() frequently.
  956.  * displaychar() is called to display input characters in the talk window.
  957.  * domodemcommand() is called to parse the modem command string.
  958.  *
  959.  * Modem format is:
  960.  *
  961.  * Bytes outside of packet - 0CCCCCCC.
  962.  *  C is character to echo to the talk window.  All characters typed during
  963.  *  a game are echoed in the local talk window and sent over the modem.
  964.  *  The ^G character should ring the bell, not be echoed into the window.
  965.  *
  966.  * Byte 1 - 000000AS  (Start packet)
  967.  *  A is Ack bit - same as seq from previous received command
  968.  *  S is Seq bit - toggle for each sent command except OK
  969.  * Byte 2 - 1KKKKKKK
  970.  *  K is checksum - sum of bytes 1,3,4.  Received packets with checksum
  971.  *   errors are ignored.
  972.  * Byte 3,4 - 1CCCRVVV  1VVVVVVV
  973.  *  C is command, R is reserved, V is value.
  974.  *   Commands:
  975.  *   0 - OK.  Value is 0.  Send in response to received command other than
  976.  *      OK.  Used to detect simultaneous actions and conflicts.  Do 
  977.  *      not accept new move or retract from user until see OK for previous
  978.  *      command.  If no OK for two seconds, send command again (with same
  979.  *      Seq).
  980.  *   1 - Denial.  Deny receipt of last command.
  981.  *   2 - Reset.  Clear board and start new game - does not reset sequence 
  982.  *       numbers
  983.  *   3 - Query.  Value is:
  984.  *       1EEEEEEE - Do you support extended command E?
  985.  *           Respond with 0x0f: Yes, 0: No
  986.  *       Others described in code below.
  987.  *   4 - Response.  Value as described above.  Sent in response to a query
  988.  *       command.
  989.  *   5 - Move. MSB of value is 0: Black, 1: White.  Rest of value is square
  990.  *      number.  0: pass, 1: lower left corner, boardsize: lower right corner.
  991.  *   6 - Take back.  Value is number of moves to take back (0-1023)
  992.  *   7 - Extended command.  Value is number of additional bytes (1-1024)
  993.  *
  994.  * Additional byte 1 - 1EEEEEEE
  995.  *   E is extended command
  996.  * Additional byte 2 - 1KKKKKKK
  997.  *   K is checksum: Sum of all additional bytes except this one.  Received
  998.  *   packets with checksum errors are ignored.
  999.  */
  1000.  
  1001.  
  1002. # define WHITEUNDO 1024
  1003. # define TAKEBACK 2048
  1004. # define PROTOCOL_VERSION 0
  1005. # define PROGRAM_ID 2
  1006. # define EXTRABUFSIZE 16
  1007. # define STARTMASK 0xfc
  1008. # define STARTVAL 0
  1009.  
  1010. /* commands */
  1011.  
  1012. # define HLACMD 0
  1013. # define DENIALCMD 1
  1014. # define RESETCMD 2
  1015. # define QUERYCMD 3
  1016. # define RESPONDCMD 4
  1017. # define MOVECMD 5
  1018. # define TAKEBACKCMD 6
  1019. # define EXTENDEDCMD 7
  1020. # define STRINGCMD 0
  1021. # define MULTICMD 15
  1022.  
  1023. /* queries */
  1024.  
  1025. # define QUERYGAME 0
  1026. # define QUERYBUF 1
  1027. # define QUERYPROTOCOL 2
  1028. # define QUERYSTONES 3
  1029. # define QUERYBTIME 4
  1030. # define QUERYWTIME 5
  1031. # define QUERYCHARSET 6
  1032. # define QUERYRULES 7
  1033. # define QUERYHANDICAP 8
  1034. # define QUERYBOARDSIZE 9
  1035. # define QUERYTIMELIMIT 10
  1036. # define QUERYCOLOR 11
  1037. # define QUERYWHO 12
  1038.  
  1039. # define QUERYSTRING 0x400
  1040. # define QUERYMULTI 0x40f
  1041.  
  1042. /* responses to QUERYWHO */
  1043.  
  1044. # define NEMESIS 1
  1045. # define MFGO 2
  1046. # define SMARTGO 3
  1047. # define GOLIATH 4
  1048. # define GOINT 5
  1049. # define STARPOL 6
  1050.  
  1051. extern int modemconnected;     /* modem connection exists */
  1052. extern char colordisplayed[];  /* color displayed on board per point */
  1053. extern list_t highlighted;     /* points that are highlighted */
  1054. extern int compmoveinprogress; /* computer is thinking */
  1055.  
  1056. list_t undocommands = EOL;  /* commands for undo */
  1057. unsigned char hostdata[4];  /* command ready to send */
  1058. unsigned char sentdata[4];  /* command most recently sent */
  1059. unsigned char recdata[4];   /* data most recently received */
  1060.  
  1061. char recextra[EXTRABUFSIZE];  /* buffer for extra received data */
  1062. char sendextra[EXTRABUFSIZE];  /* buffer to accumulate output multiple command */
  1063. int hisbuffersize = 0;  /* his extra buffer size */
  1064. int hesupportsmulti = FALSE;
  1065. int hesupportsstring = FALSE;
  1066. int whoishe = 0;
  1067. int hishandicap = 0;
  1068. int hisboardsize = 0;
  1069. int hisrules = 0;
  1070.  
  1071. int querylist[] = { QUERYWHO, QUERYRULES, QUERYHANDICAP, QUERYBOARDSIZE, -1};  /* queries to send at startup */
  1072. int nextquery = 0;      /* next query in querylist to send */
  1073. int lastquerysent = 0;  /* which query does response match? */
  1074.  
  1075. int mylastseq = 0;  /* my last sequence number */
  1076. int hislastseq = 0;  /* his last sequence number */
  1077.  
  1078. int hisprotocolversion = PROTOCOL_VERSION;
  1079. int hisprogramid = PROGRAM_ID;  /* assume talking to myself */
  1080. char waitinghighack = FALSE;  /* waiting for acknowledgement */
  1081. int querysent;   /* which query did I send */
  1082. int denycount = 0;  /* how many consecutive denials */
  1083.  
  1084. long sendtime,firstsendtime;  /* when did I send last */
  1085. int randtime = 0;  /* random timeout to resend after conflict or deny */
  1086.  
  1087. /* set up to query again when start new game */
  1088.  
  1089. newquery(){
  1090.     if(querylist[nextquery] == -1)nextquery = 1;
  1091.     }
  1092.  
  1093. /* calculate the checksum of packet p */
  1094.  
  1095. static unsigned char checksum(p)
  1096. unsigned char p[4];{
  1097.     unsigned char sum;
  1098.     int i;
  1099.     sum = p[0] + p[2] + p[3];       
  1100.     sum |= 0x80;  /* set sign bit */
  1101.     return(sum);
  1102.     }
  1103.  
  1104.  
  1105. /* put the two byte command in com into hostdata and make it ready to send */
  1106.  
  1107. static void putcommand(com)
  1108. char com[2]; {    
  1109.     hostdata[0] = 0x0;  /* make first byte */
  1110.     hostdata[2] = com[0] | 0x80;
  1111.     hostdata[3] = com[1] | 0x80;
  1112.     fillsendpacket();
  1113.     mylastseq = sentdata[0] & 1;
  1114.     sendthepacket();
  1115.     sendtime = time10();
  1116.     firstsendtime = sendtime;
  1117.     }
  1118.  
  1119. /* send him an OK */
  1120.  
  1121. static void putack(){
  1122.     unsigned char sendc[2];
  1123.     if(querylist[nextquery] != -1){  /* can send next query instead of HLA */
  1124.         putquery(querylist[nextquery]);
  1125.         return;
  1126.         }
  1127.     sendc[0] = (HLACMD << 4) | 7;
  1128.     sendc[1] = 0xff;
  1129.     putcommand(sendc);
  1130.     }
  1131.  
  1132. /* deny his command - force him to undo it - send take back 0. */
  1133.  
  1134. static void putdenial(){
  1135.     unsigned char sendc[2];
  1136.     waitinghighack = TRUE;
  1137.     sendc[0] = DENIALCMD << 4;
  1138.     sendc[1] = 0x0;
  1139.     putcommand(sendc);
  1140.     }
  1141.  
  1142. /* send a new game command */
  1143.  
  1144. void putreset(){
  1145.     unsigned char sendc[2];
  1146.     waitinghighack = TRUE;
  1147.     sendc[0] = RESETCMD << 4;
  1148.     sendc[1] = 0x0;
  1149.     putcommand(sendc);
  1150.     }
  1151.  
  1152.  
  1153. /* send a move over the modem.  mvs[ptr] contains the location of
  1154.  * the move (0-360, or PASS).  mvcolor[ptr] is the color of the move
  1155.  * (0-black, 1-white).
  1156.  */
  1157.  
  1158. void putamove(ptr)
  1159. int ptr; {
  1160.     unsigned char sendc[2];
  1161.     int val,x,y;
  1162.     if(!modemconnected)return;
  1163.     if(waitinghighack){
  1164.         outerr("Internal error - not ready for command!\n");
  1165.         return;
  1166.         }
  1167.     waitinghighack = TRUE;
  1168.  
  1169.     sendc[0] = MOVECMD << 4;
  1170.     if(mvcolor[ptr] == WHITECOLOR)sendc[0] |= 1 << 2;
  1171.     if(mvs[ptr] == PASS)
  1172.         val = 0;
  1173.     else {
  1174.         x = xval[mvs[ptr]];
  1175.         y = yval[mvs[ptr]];
  1176.         val = x+1+boardsize*(boardsize-y-1);
  1177.         }
  1178.     sendc[1] = val;
  1179.     sendc[0] |= val >> 7;
  1180.     outstatus("Sending");
  1181.     adflist(TAKEBACK,&undocommands);  /* put take back on undo list */
  1182.     putcommand(sendc);
  1183.     }
  1184.  
  1185. /* take back n moves.  must be called while moves are still in mvs[] */
  1186.  
  1187. void puttakeback(n)
  1188. int n; {
  1189.     unsigned char sendc[2];
  1190.     int i;
  1191.     if(!modemconnected)return;
  1192.     if(waitinghighack){
  1193.         outerr("Internal error - not ready for command!\n");
  1194.         return;
  1195.         }
  1196.     waitinghighack = TRUE;
  1197.     sendc[0] = TAKEBACKCMD << 4;
  1198.     sendc[0] |= n >> 7;
  1199.     sendc[1] = n & 0x7f;
  1200.     outstatus("Sending");
  1201.     for(i = msptr; i > msptr-n && i >= 0; --i)
  1202.         adflist((WHITEUNDO*(mvcolor[i] == WHITECOLOR))+mvs[i],&undocommands);
  1203.     putcommand(sendc);
  1204.     }
  1205.  
  1206.  
  1207. /* query the other program.  */
  1208.  
  1209. static void putquery(query)
  1210. int query; {
  1211.     unsigned char sendc[2];
  1212.     if(!modemconnected)return;
  1213.     if(waitinghighack){
  1214.         outerr("Internal error - not ready for command!\n");
  1215.         return;
  1216.         }
  1217.     lastquerysent = query;
  1218.     waitinghighack = TRUE;
  1219.     sendc[0] = QUERYCMD << 4;
  1220.     sendc[0] |= (query >> 7) & 7;
  1221.     sendc[1] = query;
  1222.     putcommand(sendc);
  1223.     }
  1224.  
  1225. /* respond to a query */
  1226.  
  1227. static void putresponse(){
  1228.     int query,n,i;
  1229.     unsigned char sendc[2];
  1230.     waitinghighack = TRUE;
  1231.     query = (recdata[3] & 0x7f) | (recdata[2] << 7);
  1232.     query &= 0x3ff;
  1233.     sendc[0] = RESPONDCMD << 4;
  1234.     sendc[1] = 0;
  1235.     switch(query){
  1236.           case QUERYGAME:
  1237.         sendc[1] = 1;  /* GO */
  1238.         break;
  1239.           case QUERYWHO:
  1240.         sendc[1] = PROGRAM_ID;
  1241.         break;
  1242.           case QUERYBUF:
  1243.         sendc[1] = 4 + EXTRABUFSIZE/16;
  1244.         break;
  1245.           case QUERYPROTOCOL:
  1246.         sendc[1] = PROTOCOL_VERSION;
  1247.         break;
  1248.           case QUERYSTONES:
  1249.         n = 0;
  1250.         for(i = 0; i < boardsquare; ++i)
  1251.             if(colordisplayed[i] != NOCOLOR)++n;
  1252.         sendc[1] = n;
  1253.         break;
  1254.           case QUERYCHARSET:
  1255.         sendc[1] = 1;  /* ascii */
  1256.         break;
  1257.           case QUERYRULES:
  1258.         sendc[1] = chineseflag + 1;
  1259.         break;
  1260.         case QUERYHANDICAP:
  1261.         sendc[1] = handicap;
  1262.         if(sendc[1] = 0)sendc[1] = 1;
  1263.         break;
  1264.         case QUERYBOARDSIZE:
  1265.         sendc[1] = boardsize;
  1266.         break;
  1267.           case QUERYCOLOR:
  1268.         if(cplay[WHITECOLOR])sendc[1] = 1;
  1269.         else if(cplay[BLACKCOLOR])sendc[1] = 2;
  1270.         break;
  1271.           case QUERYSTRING:
  1272.         sendc[1] = 0;  /* no string support yet */
  1273.         break;
  1274.           case QUERYMULTI:
  1275.         sendc[1] = 0;  /* no multisupport yet */
  1276.         break;
  1277.         }
  1278.     putcommand(sendc);
  1279.     }
  1280.  
  1281.  
  1282. /* domodem handles the modem interaction.  It 
  1283.  * reads an acknowledges any packet available from the
  1284.  * input port and executes the command.
  1285.  * it returns TRUE if a command was executed, or a move was taken back.
  1286.  */
  1287.  
  1288. int domodem(){
  1289.     long t;
  1290.     unsigned char seq,ack,hla;
  1291.     int command,retval = FALSE;
  1292.     if(!modemconnected)return(FALSE);
  1293.     while(getpacket()){  /* handle input packet */
  1294.         seq = recdata[0] & 1;
  1295.         ack = (recdata[0] & 2) >> 1;
  1296.         command = (recdata[2] >> 4) & 0x7;
  1297.  
  1298.         if(!waitinghighack){
  1299.             if(command == HLACMD)continue;
  1300.             else if(ack != mylastseq)continue;
  1301.             else if(seq == hislastseq)  
  1302.                 putack();  /* he missed HLA, resend */
  1303.             else {
  1304.                 hislastseq = seq;
  1305.                 domodemcommand();
  1306.                 retval = TRUE;
  1307.                 }
  1308.             }
  1309.         else {  /* waiting for OK */
  1310.             if(command == HLACMD){
  1311.                 if(ack != mylastseq || seq != hislastseq)
  1312.                     continue;  /* sequence error */
  1313.                 waitinghighack = FALSE;
  1314.                 denycount = 0;
  1315.                 gamestatus();  /* tell user ready for command */
  1316.                 killist(&undocommands);
  1317.                 }
  1318.             else if(seq == hislastseq)continue;
  1319.             else if(ack == mylastseq){
  1320.                 waitinghighack = FALSE;
  1321.                 gamestatus();  /* tell user ready for command */
  1322.                 hislastseq = seq;
  1323.                 domodemcommand();
  1324.                 killist(&undocommands);
  1325.                 retval = TRUE;
  1326.                 }
  1327.             else {
  1328.                 randtime = rand()%400+200;  /* back off for random time 2-6 seconds */
  1329.                 clearerror();
  1330.                 outerr("Conflict with opponent");
  1331.                 takeback();  /* conflict */
  1332.                 mylastseq = 1-mylastseq;
  1333.                 retval = TRUE;
  1334.                 waitinghighack = FALSE;
  1335.                 continue;
  1336.                 }
  1337.             }
  1338.         }
  1339.  
  1340.     t = time10();
  1341.  
  1342.     /* timeout - send packet again or give up */
  1343.  
  1344.     if(waitinghighack && t-sendtime > 200){  /* 2 seconds */
  1345.         if(t-firstsendtime > 6000){  /* 60 seconds */
  1346.             outerr("Missing command acknowlege, continuing");
  1347.             waitinghighack = FALSE;
  1348.             gamestatus();
  1349.             killist(&undocommands);
  1350.             }
  1351.         else {
  1352.             sendtime = t;
  1353.             sendthepacket();
  1354.             }
  1355.         }
  1356.     return(retval);
  1357.     }
  1358.  
  1359. /* fill the send packet */
  1360.  
  1361. static void fillsendpacket(){
  1362.     int i,command;
  1363.     char *dummy = NULL;
  1364.     for(i = 0; i < 4; ++i)sentdata[i] = hostdata[i];
  1365.     command = (sentdata[2] >> 4) & 0x7;
  1366.     if(command == HLACMD)
  1367.         sentdata[0] |= mylastseq;  /* set up sequence number */
  1368.     else
  1369.         sentdata[0] |= 1-mylastseq;  /* set up sequence number */
  1370.     sentdata[0] |= hislastseq << 1;  /* ack his last message */
  1371.     sentdata[1] = checksum(sentdata);
  1372.     }
  1373.  
  1374.  
  1375. /* sendthepacket sends a packet through the modem */
  1376.  
  1377. static void sendthepacket() {
  1378.     int i,command;
  1379.     char buf[100];
  1380.     for(i = 0; i < 4; ++i)
  1381.         putmodem(sentdata[i]);
  1382. #ifndef PCMIN
  1383.     if(debug != 0){
  1384.         command = (sentdata[2] >> 4) & 0x7;
  1385.         sprintf(buf,"sent cmd %d, ack %d, seq %d\n",command,(sentdata[0]&2)>>1,sentdata[0] & 1);
  1386.         outerr(buf);
  1387.         }
  1388. #endif
  1389.     }
  1390.  
  1391.  
  1392. /* get packet assembles a packet from the modem input.  It returns TRUE
  1393.  * if a packet has been assembled in receivepacket, and FALSE otherwise.
  1394.  * if it returns FALSE, there are no more characters in the modem input buffer.
  1395.  * it handles characters outside of packets.
  1396.  */
  1397.  
  1398. static char nextpacketbyte;
  1399. static int nextextrabyte,numextrabytes;
  1400.  
  1401. static char receivestate = 0;
  1402. /* 0 - waiting for start of packet
  1403.  * 1 - reading packet
  1404.  * 2 - reading extra data
  1405.  */
  1406.  
  1407. # define COUNTLIMIT 1000
  1408.  
  1409. int getpacket(){
  1410.     unsigned char c;
  1411.     int command,count = 0,flag;
  1412.     char buf[100];
  1413.     while((flag = getmodem(&c)) || receivestate && count++ < COUNTLIMIT){
  1414.         if(!flag)continue;
  1415.                 /* get a character from the modem if available */
  1416.                 /* if get first character of a packet, spin wait for rest */
  1417.         switch(receivestate){
  1418.               case 0:  /* idle, looking for start of packet */
  1419. #ifndef PCMIN
  1420.             if(debug != 0 && (c&STARTMASK) != STARTVAL){
  1421.                 sprintf(buf,"%x\n",c);
  1422.                 outerr(buf);
  1423.                 }              
  1424. #endif                
  1425.             if((c&STARTMASK) == STARTVAL){  /* start ofpacket */
  1426.                 receivestate = 1;
  1427.                 recdata[0] = c;
  1428.                 nextpacketbyte = 1;
  1429.                 }
  1430.             else if(c&0x80)break;  /* error */
  1431.             else displaychar(c,1);  /* character outside of packet, display it */
  1432.             break;
  1433.               case 1:  /* reading packet */
  1434.             if((c&0x80) == 0){ /* error */
  1435.                 if((c & STARTMASK) == STARTVAL){
  1436.                     receivestate = 1;
  1437.                     recdata[0] = c;
  1438.                     nextpacketbyte = 1;
  1439.                     break;
  1440.                     }
  1441.                 else {
  1442.                     receivestate = 0;
  1443.                     displaychar(c,1);
  1444.                     }
  1445.                 break;
  1446.                 }
  1447.             recdata[nextpacketbyte++] = c;
  1448.             if(nextpacketbyte == 4){  /* check for extra bytes */
  1449.                 command = (recdata[2] >> 4) & 0x7;
  1450.                 if(command == EXTENDEDCMD){ 
  1451.                     receivestate = 2;
  1452.                     nextextrabyte = 0;
  1453.                     numextrabytes = recdata[3] & 0x7f;
  1454.                     numextrabytes |= ((int)recdata[2] & 7) << 7;
  1455.                     }
  1456.                 else { /* done, no extra bytes */
  1457.                     receivestate = 0;
  1458. #ifndef PCMIN
  1459.                     if(debug != 0){
  1460.                         command = (recdata[2] >> 4) & 7;
  1461.                         sprintf(buf,"getpacket: cmd %d ack %d seq %d\n",command,(recdata[0] & 2)>>1, recdata[0] & 1);
  1462.                         outerr(buf);
  1463.                         if(checksum(recdata) != recdata[1])
  1464.                             outerr("checksum error");
  1465.                         }
  1466. #endif
  1467.                     return(checksum(recdata) == recdata[1]);  
  1468.                     /* good packet if checksum matches */
  1469.                     }
  1470.                 }
  1471.             break;
  1472.               case 2:
  1473.             if((c&0x80) == 0){ /* error */
  1474.                 if((c & STARTMASK) == STARTVAL){
  1475.                     receivestate = 1;
  1476.                     recdata[0] = c;
  1477.                     nextpacketbyte = 1;
  1478.                     break;
  1479.                     }
  1480.                 else {
  1481.                     receivestate = 0;
  1482.                     displaychar(c,1);
  1483.                     }
  1484.                 break;
  1485.                 }
  1486.             if(nextextrabyte < EXTRABUFSIZE)  /* dump extras */
  1487.                 recextra[nextextrabyte] = c;
  1488.             nextextrabyte++;
  1489.             if(nextextrabyte == numextrabytes){  /* done */
  1490.                 receivestate = 0;
  1491.                 return(numextrabytes <= EXTRABUFSIZE && checksum(recdata) == recdata[1]);
  1492.                 }
  1493.             break;
  1494.             }
  1495.         }
  1496.     return(FALSE);
  1497.     }
  1498.  
  1499. /* output a local message and send it over the modem */
  1500.  
  1501. putmsg(s)
  1502. char *s; {
  1503.     outerr(s);
  1504.     while(*s != 0){
  1505.         putmodem(*s);
  1506.         s++;
  1507.         }
  1508.     }
  1509.  
  1510. /* execute commands received over the modem.  If waiting for high level
  1511.  * ack, ignore command and undo save commands
  1512.  */
  1513.  
  1514. static void domodemcommand(){
  1515.     int s,num,i,command,x,y;
  1516.     char buf[100];
  1517.  
  1518.  
  1519.     command = (recdata[2] >> 4) & 0x7;
  1520.  
  1521. #ifndef PCMIN
  1522.     if(debug != 0){
  1523.         sprintf(buf,"got cmd %d\n",command);
  1524.         outerr(buf);
  1525.         }
  1526. #endif
  1527.  
  1528.     if(recdata[2] & 8){  /* reserved bit - don't know what this means */
  1529.         putdenial();
  1530.         return;
  1531.         }
  1532.  
  1533.     switch(command){
  1534.           case  DENIALCMD:
  1535.         putack();
  1536.         takeback();
  1537.         denycount++;
  1538.         if(denycount > 3){
  1539.             turnoffcplay();
  1540.             outerr("Opponent denying moves.  Stopping computer play");
  1541.             }
  1542.         break;
  1543.           case QUERYCMD:
  1544.         putresponse();
  1545.         break;
  1546.           case RESETCMD:
  1547.         if(compmoveinprogress){  /* can't take back moves while computer is thinking */
  1548.             putdenial();
  1549.             putmsg("\n\nComputer thinking, command denied.\n");
  1550.             break;
  1551.             }
  1552.         nextquery = 0;
  1553.         putack();
  1554.         mailgame();
  1555. #ifdef PC           
  1556.         mouseShow(HIDECURSOR);  /* hide the mouse */
  1557. #endif        
  1558.         newgame();  /* initialize for new game */
  1559. #ifdef PC        
  1560.         if(havemouse)mouseShow(SHOWCURSOR);
  1561. #endif    
  1562.         break;
  1563.  
  1564.            case TAKEBACKCMD:  /* take back */
  1565.         if(compmoveinprogress){  /* can't take back moves while computer is thinking */
  1566.             putdenial();
  1567.             putmsg("\n\nComputer thinking, command denied.\n");
  1568.             break;
  1569.             }
  1570.         num = recdata[3] & 0x7f;
  1571.         num |= (recdata[2] & 7) << 7;
  1572.  
  1573.         if(num > msptr-handicap){
  1574.             putdenial();
  1575.             putmsg("\n\nCan't take back handicap stones.\n");
  1576.             break;
  1577.             }
  1578.  
  1579.         putack();
  1580. #ifdef PC           
  1581.         mouseShow(HIDECURSOR);
  1582. #endif        
  1583.         if(num == 0)break;
  1584.         clearerror();
  1585.         outerr("Retracting move\n");
  1586.         for(i = 0; i < num; ++i){
  1587.             if(msptr > 0)
  1588.                 retractmove(FALSE);  /* retract but don't send */
  1589.             }
  1590.         outerr("Done\n");
  1591.         gamestatus();
  1592. #ifdef PC        
  1593.         if(havemouse)mouseShow(SHOWCURSOR);
  1594. #endif    
  1595.         break;
  1596.           case MOVECMD:
  1597.         if(compmoveinprogress){  /* can't make moves while computer is thinking */
  1598.             putdenial();
  1599.             putmsg("\n\nComputer thinking, command denied.\n");
  1600.             break;
  1601.             }
  1602.         mvcolor[msptr] = (recdata[2] & 4) >> 2;
  1603.         s = ((int)recdata[2] << 7) | (recdata[3] & 0x7f);
  1604.         s &= 0x1ff;
  1605.         if(s == 0)
  1606.             mvs[msptr] = PASS;
  1607.         else {
  1608.             s--;
  1609.             x = s%boardsize;
  1610.             y = s/boardsize;
  1611.             mvs[msptr] = boardsize*(boardsize-y-1)+x;
  1612.             }
  1613.         if(mvs[msptr] != PASS && (mvs[msptr] >= boardsquare ||
  1614.             mvs[msptr] < 0 || S_COLOR(mvs[msptr]) != NOCOLOR)){  /* got illegal move */
  1615.             putdenial();
  1616.             putmsg("\n\nIllegal move: denied.\n");
  1617.             break;
  1618.             }
  1619.         putack();
  1620. #ifdef PC          
  1621.         mouseShow(HIDECURSOR);
  1622. #endif        
  1623.         check(FALSE);  /* make the move */
  1624.         gamestatus();
  1625. #ifdef PC        
  1626.         if(havemouse)mouseShow(SHOWCURSOR);
  1627. #endif        
  1628.         break;
  1629.           case RESPONDCMD:
  1630.         num = recdata[3] & 0x7f;
  1631.         num |= (recdata[2] & 7) << 7;
  1632.         nextquery++;  /* prepare to send next query */
  1633.         switch(lastquerysent){
  1634.               case QUERYBUF:
  1635.             hisbuffersize = num*16 + 4;
  1636.             break;
  1637.               case QUERYSTRING:
  1638.             hesupportsstring = num;
  1639.             break;
  1640.               case QUERYMULTI:
  1641.             hesupportsmulti = num;
  1642.             break;
  1643.               case QUERYHANDICAP:
  1644.             hishandicap = num;
  1645.             if(hishandicap == 1 && handicap != 0 || hishandicap > 1 && hishandicap != handicap){
  1646.                 if(hishandicap == 1)hishandicap = 0;
  1647.                 sprintf(buf,"His handicap (%d) and your handicap (%d) are different!",hishandicap,handicap);
  1648.                 outerr(buf);
  1649.                 }
  1650.             break;
  1651.             case QUERYBOARDSIZE:
  1652.             hisboardsize = num;
  1653.             if(hisboardsize != 0 && hisboardsize != boardsize){
  1654.                 sprintf(buf,"His board size (%d) and your board size (%d) are different!",hisboardsize,boardsize);
  1655.                 outerr(buf);
  1656.                 }
  1657.             break;
  1658.             case QUERYRULES:
  1659.             hisrules = num;
  1660.             if(hisrules != 0 && hisrules-1 != chineseflag)
  1661.                 outerr("He is playing different rules than you");
  1662.             break;
  1663.               case QUERYWHO:
  1664.             whoishe = num;
  1665.             outerr("Connected to ");
  1666.             switch(whoishe){
  1667.                   case NEMESIS:
  1668.                 outerr("Nemesis");
  1669.                 break;
  1670.                   case MFGO:
  1671.                 outerr("Many Faces of Go");
  1672.                 break;
  1673.                   case SMARTGO:
  1674.                 outerr("Smart Go Board");
  1675.                 break;
  1676.                   case GOLIATH:
  1677.                 outerr("Goliath");
  1678.                 break;
  1679.                   case GOINT:
  1680.                 outerr("Go Intellect");
  1681.                 break;
  1682.                   case STARPOL:
  1683.                 outerr("Star of Poland");
  1684.                 break;
  1685.                   default:
  1686.                 outerr("Unknown");
  1687.                 break;
  1688.                 }
  1689.             break;
  1690.             }
  1691.         putack();  /* might send next query */
  1692.         break;
  1693.           default:
  1694.         putdenial();  /* tell him I don't understand his command */
  1695.         }
  1696.     }
  1697.  
  1698. /* take back the last command */
  1699.  
  1700. static void takeback(){
  1701.     list_t ptr;
  1702. #ifdef PC           
  1703.     mouseShow(HIDECURSOR);
  1704. #endif        
  1705.     for(ptr = undocommands; ptr != EOL; ptr = link[ptr])
  1706.         if(list[ptr] == TAKEBACK)
  1707.             if(msptr > 0){
  1708.                 retractmove(FALSE);  /* retract, but don't send */
  1709.                 }
  1710.         else {
  1711.             mvcolor[msptr] = (list[ptr] & WHITEUNDO) == WHITEUNDO;
  1712.             mvs[msptr] = list[ptr] & 511;
  1713.             check(FALSE);
  1714.             gamestatus();
  1715.             }
  1716. #ifdef PC        
  1717.     if(havemouse)mouseShow(SHOWCURSOR);
  1718. #endif    
  1719.     killist(&undocommands);
  1720.     gamestatus();
  1721.     }
  1722.  
  1723.  
  1724.  
  1725. /**************************************************************************
  1726.  *
  1727.  *   Serial com port for IBM-PC
  1728.  *
  1729.  **************************************************************************/
  1730.  
  1731.  
  1732. # define RS232 0x14 /* serial communication BIOS interrrupt */
  1733. # define COMBUFSIZE 68
  1734.  
  1735. char icombuf[COMBUFSIZE];
  1736.  
  1737. int icbfront = 0, icbback = 0;
  1738. int vector = 0;  /* interrupt vector substituted */
  1739. unsigned int portaddr;  /* com port base address */
  1740. unsigned int portnum;  /* com port number */
  1741. int mask;  /* interrupt enable mask for 8259 */
  1742.  
  1743. void modemintoff(){
  1744.     int tmp;
  1745.     if(!modemconnected)return;
  1746.     _disable();
  1747.     tmp = inp(0x21);
  1748.     tmp &= ~mask;          /* turn off com port interrupt mask */
  1749.     outp(0x21,tmp);
  1750.     _enable();
  1751.     }
  1752.  
  1753. void modeminton(){
  1754.     int tmp;
  1755.     if(!modemconnected)return;
  1756.     _disable();
  1757.     tmp = inp(0x21);
  1758.     tmp |= mask;          /* turn off com port interrupt mask */
  1759.     outp(0x21,tmp);
  1760.     _enable();
  1761.     }
  1762.  
  1763.  
  1764. static void (_interrupt _far *old)();  /* old handler address */
  1765.  
  1766. /* handle COM port interrupt for "Data received".
  1767.  * IMPORTANT NOTE:  This interrupt handler assumes a vaid stack exists.
  1768.  * It will cause the PC to crash if called when there is not enough
  1769.  * stack space (about 20 or 30 bytes).  In particular if it is called
  1770.  * while the microsoft overlay manager is executing I have seen it cause
  1771.  * a crash.  I recommend disabling the com port interrupts before loading
  1772.  * an overlay.  Alternatively, you can rewrite it to use a local stack
  1773.  * which requires assembler.
  1774.  */
  1775.  
  1776.  
  1777. void _interrupt _far comhandler(){
  1778.     int intid,intr,lsr,msr,count = 0;;
  1779.     _enable();   /* enable higher priority interrupts */
  1780.     
  1781.     while(TRUE){  /* process all pending interrupts */
  1782.         intid = inp(portaddr+2);    /* get interrupt type */
  1783.         lsr = inp(portaddr+3);  /* clear any overrun condition */
  1784.         msr = inp(portaddr+6);  /* clear any modem signal condition */
  1785.         
  1786.         if((intid & 1) == 1){              /* no interrupts */
  1787.             outp(0x20,0x20);       /* reenable interrupts */
  1788.             return;
  1789.             }
  1790.  
  1791.         if(intid >= 4){  /* reenable interrupt if needed */
  1792.             intr = inp(portaddr+1);
  1793.             if((intr & 2) == 0)outp(portaddr+1,intr | 2);
  1794.             }
  1795.             
  1796.  
  1797.         if(intid == 4){  /* receive data available */
  1798.             icombuf[icbfront] = inp(portaddr);  /* stick char in front of queue */
  1799.             icbfront++;
  1800.             if(icbfront == COMBUFSIZE)
  1801.                 icbfront = 0;
  1802.             if(icbfront == icbback){  /* overrun - throw away old char */
  1803.                 icbfront--;
  1804.                 if(icbfront == -1)icbfront = COMBUFSIZE-1;
  1805.                 }
  1806.             }
  1807.             
  1808.         }
  1809.     }
  1810.  
  1811. /* initialize modem.  comport contains the com port number (1-2) */
  1812. /* baudrate is 0-300, 1-1200, 2-2400, 3-4800, 4-9600 */
  1813. /* return FALSE if no com port */
  1814.  
  1815. initmodem(comport,baudrate,modemstring)
  1816. int comport,baudrate; 
  1817. char *modemstring; {
  1818.     union REGS regs;
  1819.     char buf[80],dummy,tmp;
  1820.     unsigned int init = _COM_CHR8 | _COM_STOP1 | _COM_NOPARITY;  /* no parity, 1 stop bit, 8 bits */
  1821.     switch(baudrate){
  1822.         case 0:
  1823.             init |= _COM_300;
  1824.             break;
  1825.         case 1:
  1826.             init |= _COM_1200;
  1827.             break;
  1828.         case 2:
  1829.             init |= _COM_2400;
  1830.             break;
  1831.         case 3:
  1832.             init |= _COM_4800;
  1833.             break;
  1834.         case 4:
  1835.             init |= _COM_9600;
  1836.             break;
  1837.         }
  1838.     if(comport == 1){
  1839.         portnum = 0;
  1840.         portaddr = *((short far *)0x400000L);
  1841.         vector = 0xc;
  1842.         mask = 0x10;
  1843.         }
  1844.     else {
  1845.         portnum = 1;
  1846.         portaddr = *((short far *)0x400002L);
  1847.         vector = 0xb;
  1848.         mask = 0x8;
  1849.         }
  1850.     if(portaddr == 0){
  1851.         outerr("Serial port not found");
  1852.         return(FALSE);
  1853.         }
  1854.  
  1855.     old = _dos_getvect(vector);  /* save old vector */
  1856.     _disable();
  1857.     _dos_setvect(vector,comhandler); /* install my vector */
  1858.     _enable();
  1859.     _bios_serialcom(_COM_INIT,portnum,init);  /* initialize uart */
  1860.  
  1861.     _disable();
  1862.     outp(portaddr+4,11);    /* initialize modem control register */
  1863.                             /* bit 3 enables ints */
  1864.                             /* bit 0,1 DTR and RTS */
  1865.     outp(portaddr+1,1);      /* enable input interrupts */
  1866.     tmp = inp(0x21);         /* enable interrupt through 8259 */
  1867.     tmp &= ~mask;              /* clear interrupt mask bit */
  1868.     outp(0x21,tmp);
  1869.     _enable();
  1870.     
  1871.     modemconnected = TRUE;
  1872.     while(*modemstring != 0)putmodem(*modemstring++);
  1873.         /* put the call or answer string */
  1874.     while(getmodem(&dummy));  /* eat the chars echoed from the modem */
  1875.     }
  1876.  
  1877. /* turn off the modem and remove the interrupt handler */
  1878.     
  1879. offmodem(){
  1880.     unsigned char tmp;
  1881.     if(vector != 0){
  1882.         _disable();
  1883.         outp(portaddr+1,0);  /* disable all serial interrupts */
  1884.         outp(portaddr+4,0);  /* turn off modem controls */
  1885.         tmp = inp(0x21);
  1886.         tmp |= mask;          /* turn off com port interrupt mask */
  1887.         outp(0x21,tmp);
  1888.         _dos_setvect(vector,old);  /* restore interrupt vector */
  1889.         vector = 0;
  1890.         _enable();
  1891.         outerr("Modem disabled");
  1892.         }
  1893.     if(modemconnected && hayesflag){
  1894.         putmodem(modemclose);
  1895.         putmodem('\r');
  1896.         hayesflag = FALSE;
  1897.         }
  1898.     modemconnected = FALSE;
  1899.     waitinghighack = FALSE;
  1900.     }
  1901.     
  1902.  
  1903. /* get a charcter from the serial port input buffer.  Return FALSE if
  1904.  * the buffer is empty
  1905.  */
  1906.     
  1907. getmodem(c)
  1908. char *c; {
  1909.     if(icbback == icbfront)return(FALSE);  /* empty buffer */
  1910.     *c = icombuf[icbback];
  1911.     _disable();
  1912.     icbback++;
  1913.     if(icbback == COMBUFSIZE)icbback = 0;
  1914.     _enable();
  1915.     return(TRUE);
  1916.     }
  1917.  
  1918. /* send char c to output buffer */
  1919.     
  1920. putmodem(c)
  1921. unsigned char c; {
  1922.     int status;
  1923.     do {
  1924.         status = inp(portaddr+5);
  1925.         } while(!(status & 0x20));  /* wait for transmit register empty */
  1926.     outp(portaddr,c);
  1927.     }    
  1928.  
  1929.  
  1930.  
  1931.